package org.summerframework.component.quartz.config;

import org.summerframework.component.quartz.annotation.BeanMethodInvokingJobDetailFactoryBean;
import org.summerframework.component.quartz.annotation.QuartzJob;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.quartz.*;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author 石超
 * @version v1.0.0
 */
public class QuartzPostProcessor implements BeanFactoryAware, BeanDefinitionRegistryPostProcessor {

    private ListableBeanFactory beanFactory;

    public static final List<QuartzJobDTO> quartzJobs = new ArrayList<>();

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
        // 获取所有bean name
        String[] beanNames = beanFactory.getBeanNamesForType(Object.class);

        for (String beanName : beanNames) {
            Class<?> targetClass = beanFactory.getType(beanName);
            Map<Method, QuartzJob> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
                    (MethodIntrospector.MetadataLookup<QuartzJob>) method -> {
                        QuartzJob quartzJob = AnnotatedElementUtils.getMergedAnnotation(
                                method, QuartzJob.class);
                        return quartzJob;
                    });

            // 循环判断是否标记了MyTriggerType注解
            if (!annotatedMethods.isEmpty()) {
                Object targetObject = beanFactory.getBean(beanName);
                // 获取时间表达式

                // 确定标记了MyTriggerMethod注解的方法名
                for (Method method : annotatedMethods.keySet()) {
                    if (method.isAnnotationPresent(QuartzJob.class)) {
                        String targetMethod = method.getName();
                        QuartzJob quartzJob = annotatedMethods.get(method);
                        String cronExpression = quartzJob.cron();
                        // 注册定时器业务类
                        // 声明包装业务类
                        final String baseName = beanName + "_" + targetMethod;
                        final String jobDetailName = baseName + "_task";
                        final String name = beanName + "_" + targetMethod + "_trigger";

                        //System.out.println(beanName + "_" + targetMethod+"-----cron=" + cronExpression);
                        // 获取JobDetail
                        beanDefinitionRegistry.registerBeanDefinition(jobDetailName, BeanDefinitionBuilder.genericBeanDefinition(BeanMethodInvokingJobDetailFactoryBean.class, () -> {
                            BeanMethodInvokingJobDetailFactoryBean jobDetailFactoryBean = new BeanMethodInvokingJobDetailFactoryBean();
                            jobDetailFactoryBean.setTargetObject(targetObject);
                            jobDetailFactoryBean.setTargetMethod(targetMethod);
                            jobDetailFactoryBean.setBeanName(jobDetailName);
                            jobDetailFactoryBean.setName(jobDetailName);
                            jobDetailFactoryBean.setTargetBeanName(beanName);
                            jobDetailFactoryBean.setConcurrent(quartzJob.concurrent());
                            jobDetailFactoryBean.setGroup(quartzJob.group());

                            return jobDetailFactoryBean;
                        }).getBeanDefinition());

                        beanDefinitionRegistry.registerBeanDefinition(name, BeanDefinitionBuilder.genericBeanDefinition(CronTriggerFactoryBean.class, () -> {
                            // 声明定时器
                            CronTriggerFactoryBean cronTriggerBean = new CronTriggerFactoryBean();
                            cronTriggerBean.setJobDetail(beanFactory.getBean(jobDetailName, JobDetail.class));
                            cronTriggerBean.setCronExpression(cronExpression);
                            cronTriggerBean.setName(name);
                            cronTriggerBean.setBeanName(name);
                            cronTriggerBean.setGroup(quartzJob.group());
                            cronTriggerBean.setMisfireInstruction(quartzJob.misfireInstruction());

                            return cronTriggerBean;
                        }).getBeanDefinition());

                        quartzJobs.add(new QuartzJobDTO(quartzJob, beanName, name, jobDetailName, method));
                    }
                }
            }
        }
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (ListableBeanFactory) beanFactory;
    }

    @Getter
    @Setter
    @AllArgsConstructor
    public static class QuartzJobDTO {
        private QuartzJob quartzJob;
        private String beanName;
        private String triggerName;
        private String jobDetailName;
        private Method method;
    }
}
